@page "/indentity/users"
@inherits OwningComponentBase
@implements IDisposable
@using Blazor.Server.UI.Components.Dialogs
@using CleanArchitecture.Blazor.Application.Common.Interfaces.Identity
@using CleanArchitecture.Blazor.Infrastructure.Constants.Role
@using CleanArchitecture.Blazor.Infrastructure.Services
@using CleanArchitecture.Blazor.Infrastructure.Services.Authentication
@using Microsoft.AspNetCore.Components.Server.Circuits
@using Microsoft.AspNetCore.Identity
@inject IStringLocalizer<Users> L
@attribute [Authorize(Policy = Permissions.Users.View)]
<PageTitle>@Title</PageTitle>
<style>
    .mud-table-toolbar {
        height: 84px !important;
    }
</style>
<ErrorBoundary>
    <ChildContent>

        <MudTable Items="UserList"
                  FixedHeader="true"
                  FixedFooter="true"
                  Height="calc(100vh - 265px)"
                  Hover="true"
                  MultiSelection="true"
                  SelectedItems="SelectItems"
                  SortLabel="@L["Sort By"]"
                  Loading="@_loading"
                  Filter="new Func<UserModel,bool>(_quickFilter)">
            <ToolBarContent>
                <div class="justify-start pt-3">
                    <MudText Typo="Typo.h6">Users</MudText>
                    <MudHidden Breakpoint="Breakpoint.SmAndDown">
                        <MudButton DisableElevation Variant="Variant.Outlined"
                                   Size="Size.Small"
                                   Disabled="@_loading"
                                   OnClick="@(()=>OnRefresh())"
                                   StartIcon="@Icons.Material.Filled.Refresh" IconColor="Color.Surface" Color="Color.Primary"
                                   Style="margin-right: 4px; margin-bottom:4px">@L["Refresh"]</MudButton>
                        @if (_canCreate)
                        {
                            <MudButton DisableElevation Variant="Variant.Outlined" Color="Color.Primary"
                                   StartIcon="@Icons.Material.Filled.Add"
                                   Size="Size.Small"
                                   Style="margin-right: 4px; margin-bottom:4px"
                                   OnClick="OnCreate"
                                   IconColor="Color.Surface">@L["Create"]</MudButton>
                        }
                        @if (_canDelete)
                        {
                            <MudButton DisableElevation Variant="Variant.Outlined" Color="Color.Error"
                                   StartIcon="@Icons.Material.Filled.Delete"
                                   Disabled="@(!(SelectItems.Count>0))"
                                   Size="Size.Small"
                                   Style="margin-right: 4px; margin-bottom:4px"
                                   OnClick="OnDeleteChecked"
                                   IconColor="Color.Surface">@L["Delete"]</MudButton>
                        }
                        @if (_canImport)
                        {
                            <InputFile id="importdataInput" OnChange="OnImportData" hidden accept=".xlsx" />
                            <MudButton DisableElevation Variant="Variant.Outlined" Color="Color.Primary"
                                   StartIcon="@Icons.Material.Filled.Upload"
                                   Size="Size.Small"
                                   Style="margin-right: 4px; margin-bottom:4px"
                                   for="importdataInput"
                                   HtmlTag="label"
                                   IconColor="Color.Surface">@L["Import Data"]</MudButton>
                        }
                        @if (_canExport)
                        {
                            <MudButton DisableElevation Variant="Variant.Outlined" Color="Color.Primary"
                                   StartIcon="@Icons.Material.Filled.Download"
                                   Size="Size.Small"
                                   Style="margin-right: 4px; margin-bottom:4px"
                                   IconColor="Color.Surface">@L["Export Data"]</MudButton>
                        }
                    </MudHidden>
                    <MudHidden Breakpoint="Breakpoint.SmAndDown" Invert="true">
                        <MudMenu AnchorOrigin="Origin.BottomLeft" StartIcon="@Icons.Filled.KeyboardCommandKey" EndIcon="@Icons.Filled.KeyboardArrowDown" Label="@L["Action"]" Color="Color.Primary" Variant="Variant.Filled">
                            <MudMenuItem Disabled="@_loading" OnClick="@(()=>OnRefresh())">@L["Refresh"]</MudMenuItem>
                            @if (_canCreate)
                            {
                                <MudMenuItem OnClick="OnCreate">@L["Create"]</MudMenuItem>
                            }
                            @if (_canDelete)
                            {
                                <MudMenuItem OnClick="OnDeleteChecked">@L["Delete"]</MudMenuItem>
                            }
                            @if (_canImport)
                            {
                                <InputFile id="importdataInput" OnChange="OnImportData" hidden accept=".xlsx" />
                                <MudMenuItem for="importdataInput"
                                         HtmlTag="label">@L["Import Data"]</MudMenuItem>
                            }
                            @if (_canExport)
                            {
                                <MudMenuItem OnClick="OnExport">@L["Export Data"]</MudMenuItem>
                            }
                        </MudMenu>
                    </MudHidden>
                </div>
                <MudSpacer />
                @if (_canSearch)
                {
                    <MudTextField @bind-Value="_searchString" Immediate="true" FullWidth="false"
                              Placeholder="@(L["Search for user name"])" Adornment="Adornment.End"
                              AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0 mb-3">
                    </MudTextField>
                }
            </ToolBarContent>
            <ColGroup>
                <col style="width:50px;" />

            </ColGroup>
            <HeaderContent>
                <MudTh Style="width:80px">@L["Actions"]</MudTh>
                <MudTh><MudTableSortLabel SortBy="new Func<ApplicationUser, object>(x=>x.Site)">@L["Site"]</MudTableSortLabel></MudTh>
                <MudTh><MudTableSortLabel SortBy="new Func<ApplicationUser, object>(x=>x.UserName)">@L["User Name"]</MudTableSortLabel></MudTh>
                <MudTh><MudTableSortLabel SortBy="new Func<ApplicationUser, object>(x=>x.DisplayName)">@L["Dispaly Name"]</MudTableSortLabel></MudTh>
                <MudTh><MudTableSortLabel SortBy="new Func<ApplicationUser, object>(x=>x.Email)">@L["Email"]</MudTableSortLabel></MudTh>
                <MudTh><MudTableSortLabel SortBy="new Func<ApplicationUser, object>(x=>x.PhoneNumber)">@L["Phone Number"]</MudTableSortLabel></MudTh>
                <MudTh><MudTableSortLabel SortBy="new Func<ApplicationUser, object>(x=>x.IsActive)">@L["Is Active"]</MudTableSortLabel></MudTh>
                <MudTh>@L["Lock Status"]</MudTh>
            </HeaderContent>
            <RowTemplate>
                <MudTd DataLabel="@L["Actions"]">
                    @if (_canEdit || _canManageRoles || _canRestPassword || _canActive)
                    {
                        <MudMenu Label="@L["Actions"]" Variant="Variant.Filled" DisableElevation="true"
                             Size="Size.Small"
                             Dense="true"
                             EndIcon="@Icons.Filled.KeyboardArrowDown" IconColor="Color.Info" Direction="Direction.Left"
                             OffsetX="true">
                            @if (_canEdit)
                            {
                                <MudMenuItem OnClick=@(()=>OnEdit(context))>@L["Edit"]</MudMenuItem>
                            }
                            @if (_canActive)
                            {
                                @if (context.IsActive)
                                {
                                    <MudMenuItem OnClick=@(()=>OnSetActive(context))>@L["Set Inactive"]</MudMenuItem>
                                }
                                else
                                {
                                    <MudMenuItem OnClick=@(()=>OnSetActive(context))>@L["Set Active"]</MudMenuItem>
                                }

                            }
                            @if (_canRestPassword)
                            {
                                <MudMenuItem OnClick=@(()=>OnResetPassword(context))>@L["Rest Password"]</MudMenuItem>
                            }

                        </MudMenu>
                    }
                    else
                    {
                        <MudButton Variant="Variant.Filled" DisableElevation="true"
                               StartIcon="@Icons.Material.Filled.DoNotTouch" IconColor="Color.Secondary" Size="Size.Small"
                               Color="Color.Surface">
                            @L["No Allowed"]
                        </MudButton>
                    }
                </MudTd>
                <MudTd DataLabel="@L["Site"]">@context.Site</MudTd>
                <MudTd DataLabel="@L["User Name"]">
                    <div class="d-flex align-content-center flex-wrap">

                        <MudBadge Color="@(context.IsLive? Color.Success :Color.Error)" Overlap="false" Dot="true" Bordered="true">
                            <MudAvatar Image="@context.Avatar" Alt="@context.UserName">@context.UserName.First()</MudAvatar>
                        </MudBadge>
                        <MudText Class="ml-2 align-self-center">@context.UserName</MudText>
                    </div>
                </MudTd>
                <MudTd DataLabel="@L["Display Name"]">@context.DisplayName</MudTd>
                <MudTd DataLabel="@L["Email"]">@context.Email</MudTd>
                <MudTd HideSmall="true" DataLabel="@L["Phone Number"]">@context.PhoneNumber</MudTd>
                <MudTd HideSmall="true" DataLabel="@L["Is Active"]">
                    <MudCheckBox Checked="@context.IsActive" ReadOnly Color="Color.Secondary"></MudCheckBox>
                </MudTd>
                <MudTd HideSmall="true" DataLabel="@L["Lock Status"]">@context.LockoutEnd</MudTd>
            </RowTemplate>
            <PagerContent>
                <MudTablePager />
            </PagerContent>
        </MudTable>


    </ChildContent>
    <ErrorContent>
        <CustomError Exception="context"></CustomError>
    </ErrorContent>
</ErrorBoundary>

@code {
    [Inject]
    private IUsersStateContainer _usersStateContainer { get; set; }
    private List<UserModel> UserList = new List<UserModel>();
    private HashSet<UserModel> SelectItems = new HashSet<UserModel>();
    private string _searchString = string.Empty;
    private bool _sortNameByLength;
    public string Title { get; private set; } = "Users";

    [CascadingParameter]
    protected Task<AuthenticationState> AuthState { get; set; } = default!;
    private UserManager<ApplicationUser> _userManager { get; set; } = default!;
    [Inject]
    public CircuitHandler circuitHandler { get; set; } = default!;
    [Inject]
    private ProfileService _profileService { get; set; } = default!;
    private bool _canCreate;
    private bool _canSearch;
    private bool _canEdit;
    private bool _canDelete;
    private bool _canActive;
    private bool _canManageRoles;
    private bool _canRestPassword;
    private bool _canImport;
    private bool _canExport;
    private bool _loading;
    private MudTable<UserModel> _table = default!;
    protected override async Task OnInitializedAsync()
    {
        _userManager = ScopedServices.GetRequiredService<UserManager<ApplicationUser>>();

        Title = L["Users"];
        var state = await AuthState;
        _canCreate = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Create)).Succeeded;
        _canSearch = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Search)).Succeeded;
        _canEdit = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Edit)).Succeeded;
        _canDelete = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Delete)).Succeeded;
        _canActive = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Active)).Succeeded;
        _canManageRoles = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.ManageRoles)).Succeeded;
        _canRestPassword = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.RestPassword)).Succeeded;
        _canImport = false; // (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Import)).Succeeded;
        _canExport = false; // (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Export)).Succeeded;
        await LoadData();
        _usersStateContainer.OnChange += HandleCircuitsChanged;


    }
    public void Dispose()
    {
        _usersStateContainer.OnChange -= HandleCircuitsChanged;
    }
    public void HandleCircuitsChanged()
    {
        InvokeAsync(() =>
        {
            foreach (var session in _usersStateContainer.UsersByConnectionId)
            {
                Snackbar.Add(L[$"{session.Value} session changed."], MudBlazor.Severity.Info);
            }

        });
    }

    private Func<UserModel, bool> _quickFilter => x =>
    {
        if (string.IsNullOrWhiteSpace(_searchString))
            return true;
        if (x.UserName.Contains(_searchString, StringComparison.OrdinalIgnoreCase))
            return true;
        if (x.DisplayName.Contains(_searchString, StringComparison.OrdinalIgnoreCase))
            return true;
        if ($"{x.Email} {x.PhoneNumber} {x.Site}".Contains(_searchString))
            return true;
        return false;
    };
    private async Task OnRefresh()
    {

        await LoadData();

    }
    private async Task LoadData()
    {
        if (_loading) return;
        try
        {
            _loading = true;
            UserList = await _userManager.Users.Select(x => new UserModel()
                {
                    Avatar = x.ProfilePictureDataUrl,
                    DisplayName = x.DisplayName,
                    Email = x.Email,
                    IsActive = x.IsActive,
                    IsLive = x.IsLive,
                    PhoneNumber = x.PhoneNumber,
                    Site = x.Site,
                    UserId = x.Id,
                    UserName = x.UserName,
                    LockoutEnd = x.LockoutEnd,
                    Role = string.Join(", ", x.UserRoles.Select(x => x.Role.Name))
                }).ToListAsync();
        }
        finally
        {
            _loading = false;
        }
    }
    private async Task OnCreate()
    {
        var model = new UserFormModel() { AssignRoles = new string[] { RoleConstants.BasicRole } };
        var parameters = new DialogParameters { ["model"] = model };
        var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small, FullWidth = true };
        var dialog = DialogService.Show<_UserFormDialog>(L["Create a new user"], parameters, options);
        var result = await dialog.Result;
        if (!result.Cancelled)
        {
            var applicationUser = new ApplicationUser()
                {
                    Site = model.Site,
                    DisplayName = model.DisplayName,
                    UserName = model.UserName,
                    Email = model.Email,
                    PhoneNumber = model.PhoneNumber,
                    ProfilePictureDataUrl = model.ProfilePictureDataUrl,
                    IsActive = model.IsActive,
                };
            var password = model.Password;
            var state = await _userManager.CreateAsync(applicationUser, password);

            if (state.Succeeded)
            {
                if (model.AssignRoles is not null && model.AssignRoles.Length > 0)
                {
                    await _userManager.AddToRolesAsync(applicationUser, model.AssignRoles);
                }
                else
                {
                    await _userManager.AddToRoleAsync(applicationUser, RoleConstants.BasicRole);
                }
                UserList.Add(new UserModel()
                    {
                        Avatar = applicationUser.ProfilePictureDataUrl,
                        DisplayName = applicationUser.DisplayName,
                        Email = applicationUser.Email,
                        IsActive = applicationUser.IsActive,
                        Site = applicationUser.Site,
                        UserId = applicationUser.Id,
                        UserName = applicationUser.UserName,
                        PhoneNumber = applicationUser.PhoneNumber

                    });
                Snackbar.Add($"{L["Create successfully"]}", MudBlazor.Severity.Info);
            }
            else
            {
                Snackbar.Add($"{string.Join(",", (state.Errors.Select(x => x.Description).ToArray()))}", MudBlazor.Severity.Error);
            }
        }
    }

    private async Task OnEdit(UserModel item)
    {
        var user = await _userManager.FindByIdAsync(item.UserId);
        var roles = await _userManager.GetRolesAsync(user);
        var model = new UserFormModel()
            {
                Id = item.UserId,
                Site = item.Site,
                DisplayName = item.DisplayName,
                UserName = item.UserName,
                Email = item.Email,
                PhoneNumber = item.PhoneNumber,
                ProfilePictureDataUrl = item.Avatar,
                IsActive = item.IsActive,
                AssignRoles = roles.ToArray()
            };
        var parameters = new DialogParameters { ["model"] = model };
        var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small, FullWidth = true };
        var dialog = DialogService.Show<_UserFormDialog>(L["Edit the user"], parameters, options);
        var result = await dialog.Result;
        if (!result.Cancelled)
        {
            return;
            var state = await AuthState;

            item.Email = model.Email;
            item.PhoneNumber = model.PhoneNumber;
            item.Avatar = model.ProfilePictureDataUrl;
            item.DisplayName = model.DisplayName;
            item.Site = model.Site;
            item.UserName = model.UserName;
            item.IsActive = model.IsActive;
            if(state.User.GetUserId()==item.UserId){
                await _profileService.Update(item);
            }


            user.Email = model.Email;
            user.PhoneNumber = model.PhoneNumber;
            user.ProfilePictureDataUrl = model.ProfilePictureDataUrl;
            user.DisplayName = model.DisplayName;
            user.Site = model.Site;
            user.UserName = model.UserName;
            user.IsActive = model.IsActive;
            var identityResult = await _userManager.UpdateAsync(user);
            if (identityResult.Succeeded)
            {
                if (model.AssignRoles is not null && model.AssignRoles.Length > 0)
                {
                    await _userManager.RemoveFromRolesAsync(user, roles);
                    await _userManager.AddToRolesAsync(user, model.AssignRoles);
                }
                Snackbar.Add($"{L["Update successfully"]}", MudBlazor.Severity.Info);
            }
            else
            {
                Snackbar.Add($"{string.Join(",", (identityResult.Errors.Select(x => x.Description).ToArray()))}", MudBlazor.Severity.Error);
            }
        }
    }

    private async Task OnDeleteChecked()
    {
        string deleteContent = L["You're sure you want to delete selected items:{0}?"];
        var parameters = new DialogParameters { { nameof(DeleteConfirmation.ContentText), string.Format(deleteContent, SelectItems.Count) } };
        var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall, FullWidth = true, DisableBackdropClick = true };
        var dialog = DialogService.Show<DeleteConfirmation>(L["Delete"], parameters, options);
        var result = await dialog.Result;
        if (!result.Cancelled)
        {
            return;
            var deleteId = SelectItems.Select(x => x.UserId).ToArray();
            var deleteusers = await _userManager.Users.Where(x => deleteId.Contains(x.Id)).ToListAsync();
            foreach (var deleteuser in deleteusers)
            {
                await _userManager.DeleteAsync(deleteuser);
            }
            foreach (var item in SelectItems)
            {
                UserList.Remove(item);
            }
        }
    }

    private async Task OnSetActive(UserModel item)
    {
        var user = await _userManager.FindByIdAsync(item.UserId);
        user.IsActive = !item.IsActive;
        var state = await _userManager.UpdateAsync(user);
        item.IsActive = !item.IsActive;
        if (state.Succeeded)
        {
            Snackbar.Add($"{L["Update successfully"]}", MudBlazor.Severity.Info);
        }
        else
        {
            Snackbar.Add($"{string.Join(",", (state.Errors.Select(x => x.Description).ToArray()))}", MudBlazor.Severity.Error);
        }
    }

    private async Task OnResetPassword(UserModel item)
    {

        var model = new ResetPasswordFormModel()
            { Id = item.UserId, DisplayName = item.DisplayName, UserName = item.UserName, ProfilePictureDataUrl = item.Avatar };
        var parameters = new DialogParameters { ["model"] = model };
        var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.ExtraSmall };
        var dialog = DialogService.Show<_ResetPasswordDialog>(L["Set Password"], parameters, options);
        var result = await dialog.Result;
        if (!result.Cancelled)
        {
            return;
            var user = await _userManager.FindByIdAsync(item.UserId);
            var token = await _userManager.GeneratePasswordResetTokenAsync(user);
            var state = await _userManager.ResetPasswordAsync(user, token, model.Password);
            if (state.Succeeded)
            {
                Snackbar.Add($"{L["Reset password successfully"]}", MudBlazor.Severity.Info);
            }
            else
            {
                Snackbar.Add($"{string.Join(",", (state.Errors.Select(x => x.Description).ToArray()))}", MudBlazor.Severity.Error);
            }
        }
    }

    private Task OnExport()
    {
        return Task.CompletedTask;
    }

    private Task OnImportData(InputFileChangeEventArgs e)
    {
        return Task.CompletedTask;
    }
}
